#!/usr/bin/perl

use Data::Dumper;
use Getopt::Std;
getopts("PLWONM:S:E:Y:A:B:R:") || die "usage: $0 [-WONLP] [-M cpuspeed] [-S stretch] [-E deltas_file] [-Y y_div] [-A y_max] [-B sort_by] [-R resolution]\n";

$origin_y = 0.02;
$origin_y_step = 0.11;

$min_y = 0.2;
$max_y = 20000;
if(defined($opt_A)) {$max_y = $opt_A;}
$y_to_y2 = 100000;
$y_to_y2 = 0.0001 if $opt_M && $opt_P;

$set_max_y = 0;
$set_max_y2 = 0;

$sets = 0;
$l = 0;
while (<STDIN>) {
	chop($_);
	s/OP_//g;
	s/_/\\_/g;
	if (!m/^\#/) {
		if (m/^title/) {
			($tmp, $TITLE[$sets]) = split( /=/, $_ );
		} else {
			if (m/^set /) {
				if (m/^set opt([A-Z]*)[ \t]([0-9\.]*)$/) {
					if ($1 eq "N") {$opt_N = "1"; break;}
					if ($1 eq "O") {$opt_O = "1"; break;}
					if ($1 eq "W") {$opt_W = "1"; break;}
					if ($1 eq "M") {$opt_M = $2; break;}
					if ($1 eq "S") {$opt_S = $2; break;}
					if ($1 eq "Y") {$opt_Y = $2; break;}
					if ($1 eq "A") {$max_y = $2; break;}
					if ($1 eq "B") {$opt_B = $2; break;}
					if ($1 eq "R") {$opt_R = $2; break;}
				} else {
					if (!defined($CUSTOM_CMD[$l])) {
						$CUSTOM_CMD[$l] = [];
					}
					push(@{$CUSTOM_CMD[$l]}, $_);
				}
			} else {
				if (length($_) < 10) {
					$sets++;
					$l = 0;
				} else {
					my $b = $l;
					if (defined $opt_B && $sets) {
						for ($b = 0, my $OP_name = $OP[0][0]; $b < $#{@OP[0]} && !m/^$OP_name[ \t]/; $b++) {
							$OP_name = $OP[0][$b + 1];
						}
					}
					($OP[$sets][$b], my $num, my $delay, @tmp) = split( / /, $_ );
					$OP_NUM[$sets][$b] = 0;
					$OP_DELAY[$sets][$b] = 0;
					$OP[$sets][$b] =~ s/_/\\_/g;
					for (my $i = 0; $i <= $#{@tmp}; $i++) {
						$BUCKETS[$sets][$b][$i] = $tmp[$i];
						$OP_NUM[$sets][$b] += $tmp[$i];
						# this is for "-R 1" and "-L" only
						$OP_DELAY[$sets][$b] += 3 * $tmp[$i] * 2 ** ($i + 3);
						if ($set_max_y < $tmp[$i]) {$set_max_y = $tmp[$i];}
						if ($set_max_y2 < $tmp[$i] * 2 ** ($i + 4)) {$set_max_y2 = $tmp[$i] * 2 ** ($i + 4);}
					}
					$OP_NUM[$sets][$b] = $num if $num ne '?';
					$OP_DELAY[$sets][$b] = $delay if $delay ne '?';
					if (defined $opt_B && !$sets && $l) {
						for (; $b > 0; $b--) {
							if ($opt_B eq "name"  && $OP[0][$b] gt $OP[0][$b - 1] ||
							    $opt_B eq "num" && $OP_NUM[0][$b] < $OP_NUM[0][$b - 1] ||
							    $opt_B eq "delay" && $OP_DELAY[0][$b] < $OP_DELAY[0][$b - 1]) {
								($OP[0][$b],       $OP[0][$b - 1]      ) = ($OP[0][$b - 1],       $OP[0][$b]      );
								($OP_NUM[0][$b],   $OP_NUM[0][$b - 1]  ) = ($OP_NUM[0][$b - 1],   $OP_NUM[0][$b]  );
								($OP_DELAY[0][$b], $OP_DELAY[0][$b - 1]) = ($OP_DELAY[0][$b - 1], $OP_DELAY[0][$b]);
								($BUCKETS[0][$b],  $BUCKETS[0][$b - 1] ) = ($BUCKETS[0][$b - 1],  $BUCKETS[0][$b] );
							}
						}
					}
					$l++;
				}
			}
		}
	}
}

# Handle files with no EOF
if ($sets == 0) { $sets = 1; }

$opt_R = 1 if !defined($opt_R);

$min_x = 2.8 * $opt_R + ($opt_R - 1) * 0.3;
$max_x = 34.0 * $opt_R + ($opt_R - 1) * 0.3;
if (!defined($opt_L)) {
	$min_x += $opt_R;
	$max_x += $opt_R;
}

$stretch_y = 1;
if (defined($opt_S)) {
	$stretch_y = $opt_S;
}

$y_div = 1;
$y_div_label="";
if (defined($opt_Y)) {
	$y_div_label = sprintf(" (x%.0e)", $opt_Y);
	$y_div = $opt_Y;
}

if ($opt_P && !defined($opt_M)) {
	die "ERROR: no cpu speed is set (use -M option).\n";
}

if ($set_max_y > $max_y && !$opt_O) {
	print STDERR "WARNING: maximal gnuplot Y scale value exceeded.\n",
	"\t($set_max_y > $max_y) Try -O option.\n";
}
if ($set_max_y2 > $max_y * $y_to_y2 && !$opt_O && $opt_W) {
	print STDERR "WARNING: maximal gnuplot Y scale value exceeded.\n",
	"\t($set_max_y2 > ", $max_y * $y_to_y2, ") Try -O option.\n";
}

# Parse Deltas
if ($opt_E) {
	open (DELTF, "<$opt_E") || die "Cannot open $opt_E for reading\n";
	$esets = 0;
	$l = 0;
	while (<DELTF>) {
		chop($_);
		if (!m/^\#/) {
			if (length($_) < 10) {
				$esets++;
				$l = 0;
			} else {
				@tmp = split( / /, $_ );
				for (my $i = 0; $i <= $#{@tmp} - 3; $i++) {
					$DELTAS[$esets][$l][$i] = $tmp[$i + 3];
				}
				$l++;
			}
		}
	}
	close DELTF;
}

print <<"END_GNUPLOT";
#!/usr/local/bin/gnuplot

set term push;
set terminal postscript eps enhanced 'Helvetica' 14;

END_GNUPLOT

$size_y = ($#{@BUCKETS[0]} + 1) * $origin_y_step * $stretch_y + $origin_y_step;
$size_y += ($origin_y_step / 2) if ($opt_W);
$size_y += ($sets * $origin_y_step / 4) if ($sets > 1);

print <<"END_GNUPLOT";
set size 1.0,$size_y
set origin 0.0,0.0
set style fill solid 1.0

set logscale y
set yrange [$min_y:$max_y]
set xrange [$min_x:$max_x]
set ytics 1.0,10
#set noborder
set border 11
set grid x
set grid y
set multiplot

END_GNUPLOT

if ($sets > 1) {
	print "set boxwidth ", 1.0/($sets+1.0), "\n";
} else {
	print "set boxwidth ", 1.0, "\n";
}

if ($opt_W) {
	print "set boxwidth 0.4\n";
	print "set logscale y2\n";
	print "set ytics nomirror\n";
	$sets = 1;
	if ($opt_O) {
		$max_y = 9000;
		print "set yrange [$min_y:", $max_y*10, "]\n";
		print "set y2range [", $min_y*$y_to_y2/10, ":", $max_y*$y_to_y2, "]\n";
		print "set y2tics (";
		for (my $j = 1e4; $j < 1e9; $j *= 10) {
			print "\"";
			printf "%.0e  ", $j*10;
			print "\" ", $j, ",";
		}
		print "\"", $j, "\" ", 1e9, ")\n";
	} else {
		print "set y2range [", $min_y*$y_to_y2/10, ":", $max_y*$y_to_y2, "]\n";
		print "set y2tics ", $min_y*$y_to_y2, ",10\n";
	}
}

if ($opt_M && $opt_P) {
	$delay_units = "sec";
} else {
	$delay_units = "CPU cycles";
}

for (my $l = 0; $l <= $#{@BUCKETS[0]}; $l++) {
	print "set size 1.0, ", $origin_y_step * $stretch_y + (0.2 - $origin_y_step), "\n";
	print "set origin 0.0, $origin_y\n";
	if ($opt_N) {
		print "set label \"$OP[0][$l]\\n$OP_NUM[0][$l]\\n", sprintf("%d", $OP_DELAY[0][$l]/1000000), "M\" at graph 0.985,", 1 - 0.2/$stretch_y, " right font \"Helvetica,14\"\n";
	} else {
		print "set label \"$OP[0][$l]\" at graph 0.985,", 0.95 - 0.2/$stretch_y, " right font \"Helvetica,16\"\n";
	}
	
	# X tics and title in buckets
	if ((!$l && !defined($opt_P)) || ($l == $#{@BUCKETS[0]} && defined($opt_P))) {
		for (my $i = 5.0; $i <= $max_x; $i += 5) {
			# skip the last tic if it may overlap with the legend
			if (($i > $min_x) && (($i < $max_x - 4) || !defined($opt_P) || ($sets == 1 && !$opt_W))) {
				print "set label \'$i\' at graph ", ($i - $min_x)/($max_x - $min_x), ",";
				if (defined($opt_P)) {
					print 1 + 0.15/$stretch_y;
				} else {
					print -0.15/$stretch_y;
				}
				print " center\n";
			}
		}
		my $X_label_pos_y = -0.5/$stretch_y;
		$X_label_pos_y = 1 + 0.4/$stretch_y if defined($opt_P);
		my $X_label_pos_x = 0.5;
		$X_label_pos_x = 0.25 if (defined($opt_P) && ($sets != 1 || $opt_W));
		if (!defined($opt_L)) {
			print "set label \'Bucket (2^{n-1} <= x < 2^n CPU Cycles)\' at graph $X_label_pos_x,$X_label_pos_y center\n";
		} else {
			print "set label \'Bucket number: {/Symbol=14 \353}log_{2";
			print "^{1/$opt_R}" if $opt_R > 1;
			print "}(latency in CPU cycles){/Symbol=14 \373}\' at graph $X_label_pos_x,$X_label_pos_y center\n";
		}
	}
	# Y axes titles
	if (!$l) {
		if ($opt_W) {
			print "set label \'Number of operations$y_div_label\' at graph -0.12,", (1 + $#{@BUCKETS[0]})*0.5, " center rotate\n";
			print "set label \'Total delay ($delay_units)\' at graph 1.13,", (1 + $#{@BUCKETS[0]})*0.5, " center rotate\n";
		} else {
			print "set label \'Number of operations$y_div_label\' at graph -0.1,", (1 + $#{@BUCKETS[0]})*0.5, " center rotate\n";
		}
	}
	# X tics and title in seconds
	if (defined($opt_M) && (($l == $#{@BUCKETS[0]} && !defined($opt_P)) || (!$l && defined($opt_P)))) {
		for (my $i = 5 * $opt_R; $i <= $max_x; $i += (5 * $opt_R)) {
			# skip the last tic if it may overlap with the legend
			if (($i > $min_x) && (($i < $max_x - 4) || defined($opt_P) || ($sets == 1 && !$opt_W))) {
				$s = ((1.0 + 2.0**(1.0/$opt_R))/2.0 * (2**($i/$opt_R))) / ($opt_M * (10 ** 9));
				my $unit = "s";
				while ($s < 1) {
					if ($unit eq "s") {
						$unit = "ms";
					} elsif ($unit eq "ms") {
						$unit = "{/Symbol=14 \155}s";		#microseconds
					} elsif ($unit eq "{/Symbol=14 \155}s") {
						$unit = "ns";
					} else {
						print "ERROR: reached $s $unit";
						exit 1;
					}
					$s *= 1000;
				}
				printf "set label \'%d%s\' at graph %f,", $s, $unit, ($i - $min_x)/($max_x - $min_x);
				if (defined($opt_P)) {
					print -0.15/$stretch_y;
				} else {
					#printf("set label \'%d%s\' at graph %f,%f center\n", $s, $unit, ($i - $min_x)/($max_x - $min_x), 1 + 0.15/$stretch_y);
					print 1 + 0.15/$stretch_y;
				}
				print " center\n";
			}
		}
		print "set label \'Average bucket latency\' at graph 0.5,", -0.5/$stretch_y, " center\n" if defined($opt_P);
	}
	
	# Create major xtics
	if ($l == $#{@BUCKETS[0]}) {
		print "set border\n";
		print "set xtics mirror (";
	} else {
		print "set xtics nomirror (";	
	}
	for ($j = 5; $j < $max_x - 5; $j += 5) {
		print "\"\" ", $j, ",";
	}
	print "\"\" ", $j, ")\n";
	
	$origin_y += $origin_y_step * $stretch_y;
	
	if ($l == $#{$BUCKETS[0]} && ($sets > 1 || $opt_W)) {
		$key_y = 1.0 + 0.6 / $stretch_y;
		if (!$opt_W) {
			$key_y += ($sets - 2) * (0.2 / $stretch_y);
		}
		print "set key graph 0.93,$key_y\n";
	} else {
		print "set nokey\n";
	}
	
	print join("\n", @{$CUSTOM_CMD[$l]}) . "\n";
	
	if ($opt_W) {	
		print "plot '-' t 'Number of operations$y_div_label' with boxes fill pattern 3, '-' t 'Total delay ($delay_units)' with boxes fill pattern 6 border 1 linetype 1 axis x1y2\n";
	} else {
		print "plot '-' t '$TITLE[0]' with boxes fill pattern 3";
		#print "plot '-' t '$TITLE[0]'";
		#if ($opt_E) {
		#	print " with boxerror fill pattern 3";
		#}
		for (my $s = 1; $s < $sets; $s++) {
			my $pat = 9 - $s;
			print ", '-' t '$TITLE[$s]' with boxes fill pattern $pat border 1 linetype 1";
			#if ($opt_E) {
			#	print " with errorbars";
			#}
		}
		print "\n";
	}
		
	for (my $s = 0; $s < $sets; $s++) {
		for (my $i = 0; $i < $#{$BUCKETS[0][$l]}; $i++) {
			if ($opt_W || $sets > 1) {
				print $i + $min_x + 0.4 * $s;
			} else {
				print $i + $min_x + 0.2;
			}
			print " ", $BUCKETS[$s][$l][$i]/$y_div;
			if ($opt_E) {
				print " ", $DELTAS[$s][$l][$i];
			}
			print "\n";
		}
		print "e\n";
	}
	if ($opt_W) {
		for (my $i = 0; $i < $#{$BUCKETS[0][$l]}; $i++) {
			$W = ($BUCKETS[0][$l][$i] * 1.5 * 2 ** ($i + 4));
			$W /= ($opt_M * 2**32) if $opt_M && $opt_P;
			$W /= 10 if $opt_O;
			print $i + $min_x + 0.2 + 0.2, " $W\n";
		}
		print "e\n";
	}
	print "unset label\n";
};

print "set nomultiplot; set out; set term pop\n";
